﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;

namespace Kindohm.Life.Library
{
    public abstract class Board
    {
        int height;
        int width;
        Dictionary<Point, BoardObject> boardObjects;

        public Board()
        {
        }

        public static bool AreAdjacent(Point point1, Point point2)
        {
            Point result = point2 - point1;
            if (result.X <= 1 & result.X >= -1
                & result.Y <= 1 & result.Y >= -1)
            {
                return true;
            }
            return false;
        }

        public void AddObject(BoardObject boardObject, Point position)
        {
            if (this.boardObjects[position] == null)
            {
                this.boardObjects[position] = boardObject;
                boardObject.Board = this;
                boardObject.Position = position;
            }
            else
            {
                throw new PointOccupiedException(string.Format(
                    "The point {0},{1} already contains the board object {2}.",
                    position.X.ToString(), position.Y.ToString(), this.boardObjects[position].ToString()));
            }
        }

        public void RemoveObject(Point position)
        {
            this.boardObjects[position].Position = new Point(-1, -1);
            this.boardObjects[position] = null;
        }

        public void Initialize()
        {
            this.boardObjects = new Dictionary<Point, BoardObject>();

            for (int x = 0; x < this.width; x++)
            {
                for (int y = 0; y < this.height; y++)
                {
                    Point p = new Point();
                    p.X = x;
                    p.Y = y;
                    this.boardObjects.Add(p, null);
                }
            }
        }

        public int Height
        {
            get { return this.height; }
            set { this.height = value; }
        }

        public int Width
        {
            get { return this.width; }
            set { this.width = value; }
        }

        public Dictionary<Point, BoardObject> BoardObjects
        {
            get{ return this.boardObjects; }
        }

        public Collection<BoardObject> GetBoardObjectsWithinRange(Point start, int range, BoardObject self)
        {
            return this.GetBoardObjectsWithinRange<object>(start, range, self);
        }

        public Collection<BoardObject> GetBoardObjectsWithinRange<T>(Point start, int range, BoardObject self)
        {
            Collection<BoardObject> objects = new Collection<BoardObject>();

            int negXRange = range * -1;
            int negYRange = range * -1;

            for (int x = negXRange; x <= range; x++)
            {
                for (int y = negYRange; y <= range; y++)
                {
                    int newX = start.X + x;
                    int newY = start.Y + y;

                    if ((newX >= 0 && newX < this.width)
                        && newY >= 0 && newY < this.height)
                    {

                        Point p = new Point(newX, newY);

                        if (this.boardObjects[p] != null)
                        {
                            if (this.BoardObjects[p] is T && this.BoardObjects[p] != self)
                            {
                                objects.Add(this.boardObjects[p]);
                            }
                        }
                    }
                }
            }

            return objects;

        }

        public Collection<Point> GetEmptyPositionsWithinRange(Point start, int range)
        {

            Collection<Point> positions = new Collection<Point>();

            int negXRange = range * -1;
            int negYRange = range * -1;

            for (int x = negXRange; x <= range; x++)
            {
                for (int y = negYRange; y <= range; y++)
                {
                    int newX = start.X + x;
                    int newY = start.Y + y;

                    if ((newX >= 0 && newX < this.width)
                        && newY >= 0 && newY < this.height)
                    {

                        Point p = new Point(newX, newY);

                        if (this.boardObjects[p] == null)
                        {
                            positions.Add(p);
                        }
                    }
                }
            }

            return positions;
        }

    }
}
